package com.xigege.netty.protocolDesignAndAnalysis.customProtocol.protocol;

import com.xigege.netty.protocolDesignAndAnalysis.customProtocol.message.Message;
import com.xigege.netty.protocolDesignAndAnalysis.customProtocol.protocol.config.Config;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;

import java.util.List;

/**
 * 消息编解码器
 * 有以下要素需要解析
 * 1. 魔数，用来在第一时间判定是否是无效数据包
 * 2. 版本号，可以支持协议的升级
 * 3. 序列化算法，消息正文到底采用哪种序列化反序列化方式，可以由此扩展，例如：json、protobuf、hessian、jdk
 * 4. 指令类型，是登录、注册、单聊、群聊... 跟业务相关
 * 5. 请求序号，为了双工通信，提供异步能力
 * 6. 正文长度
 * 7. 消息正文
 */

// 父类限制不能使用@Sharable注解
// @ChannelHandler.Sharable
public class MessageCodec extends ByteToMessageCodec<Message> {

    // 编码，出站前把自定义的msg(Message)编码成out(ByteBuf)
    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
        // 1.写入魔数 四个字节
        out.writeBytes(new byte[]{1, 0, 1, 4});
        // 2.版本号 一个字节
        out.writeByte(1);
        // 3.序列化算法 一个字节
        // 枚举顺序：0代表jdk序列方式，1代表json序列化方式
        out.writeByte(Config.getSerializerAlgorithm().ordinal());
        // 4.指令类型 一个字节
        out.writeByte(msg.getMessageType());
        // 5.请求序号 四个字节
        out.writeInt(msg.getSequenceId());
        //写入一个字节的对其填充，使除消息正文的消息的字节数为2的整数倍（16）
        out.writeByte(0xff);
        // 序列化msg
        byte[] bytes = Config.getSerializerAlgorithm().serialize(msg);
        // 6.正文长度 四个字节
        out.writeInt(bytes.length);
        // 7.消息正文
        out.writeBytes(bytes);
    }

    // 解码，入站时把ByteBuf解码成Message
    // 约定解码出来的结果要存到out里面，否则接下来的handler就拿不到结果
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 读取四个字节的魔数
        int magicNum = in.readInt();
        // 读取一个字节的版本号
        byte version = in.readByte();
        // 读取一个字节的序列化算法
        byte serializeNum = in.readByte();
        // 读取一个字节的消息类型
        byte messageType = in.readByte();
        // 读取四个字节的请求序号
        int sequenceId = in.readInt();
        // 读取一个字节的对其填充
        byte padding = in.readByte();
        // 读取四个字节的正文长度
        int msgLength = in.readInt();
        // 读取消息正文
        byte[] msgBytes = new byte[msgLength];
        in.readBytes(msgBytes, 0, msgLength);

        // 反序列化msg
        // 1.找到反序列话算法
        Serializer.Algorithm algorithm = Serializer.Algorithm.values()[serializeNum];
        // 2. 根据messageType 找到真正的消息类型
        Class<? extends Message> messageClass = Message.getMessageClass(messageType);
        // 3.进行反序列化
        Message message = algorithm.deserialize(messageClass, msgBytes);

        out.add(message);

        System.out.println("魔数:" + magicNum + "\n"
                + "version:" + version + "\n"
                + "serializeNum:" + serializeNum + "\n"
                + "messageType:" + messageType + "\n"
                + "sequenceId:" + sequenceId + "\n"
                + "padding:" + padding + "\n"
                + "msgLength:" + msgLength + "\n"
                + "message:" + message);
    }
}